#include "compi/context.h"
#include "compi/p2p.h"
#include "compi/result.h"
#include "compi/tag.h"

#include <gtest/gtest.h>

#include <mpi.h>

using namespace compi;

// 基础 Result 行为：Success 与 Error
TEST(ResultTest, BasicResultSuccessAndError)
{
    auto ok = Result<void>::success("basic_op");
    EXPECT_TRUE(ok.is_success());
    EXPECT_EQ(ok.error_code(), ResultCode::Success);
    EXPECT_EQ(ok.operation(), std::string("basic_op"));

    auto err = Result<void>::error(ResultCode::Unknown, "unknown error", "basic_op");
    EXPECT_TRUE(err.is_error());
    EXPECT_EQ(err.error_code(), ResultCode::Unknown);
    EXPECT_EQ(err.error_message(), std::string("unknown error"));
    EXPECT_EQ(err.operation(), std::string("basic_op"));
}

// 非抛出等待：默认构造的请求应返回 InvalidRequest
TEST(ResultTest, TransportRequestWaitInvalidRequest)
{
    Environment env; // 初始化 MPI（若尚未初始化）
    env.set_failure_policy(FailurePolicy::ThrowException);

    TransportRequest req; // 默认构造，无实现
    auto st = req.wait();
    EXPECT_TRUE(st.is_error());
    EXPECT_EQ(st.error_code(), ResultCode::InvalidRequest);
    EXPECT_EQ(st.error_message(), std::string("TransportRequest has no implementation"));
}

// 自发自收（同一进程）：固定类型 + 命名空间过滤；验证 wait 成功并完成收发
TEST(ResultTest, WaitResultSelfSendFixed)
{
    Environment env;
    env.set_failure_policy(FailurePolicy::ThrowException);

    auto & ctx = Context::for_comm(MPI_COMM_WORLD, env);
    auto & alloc = ctx.tag_allocator();
    auto ns = alloc.create_namespace("status_self_fixed");

    const int rank = ctx.rank();
    int send_val = 42;
    int recv_val = 0;

    // 先发布发送，再匹配接收
    auto send_req = isend_fixed(ctx, rank, &send_val, MPI_INT, ns);
    auto recv_req = irecv_fixed(ctx, rank, &recv_val, MPI_INT, ns);

    auto st_recv = recv_req.wait();
    EXPECT_TRUE(st_recv.is_success());
    EXPECT_EQ(recv_val, send_val);

    auto st_send = send_req.wait();
    EXPECT_TRUE(st_send.is_success());

    // 清理上下文关联资源（标签分配器）
    Context::destroy_for_comm(MPI_COMM_WORLD);
}

// handle_error：在 LogAndContinue 下写入错误栈且不抛出
TEST(ResultTest, HandleErrorLogAndContinue)
{
    Environment env;
    env.set_failure_policy(FailurePolicy::LogAndContinue);

    auto & ctx = Context::for_comm(MPI_COMM_WORLD, env);
    EXPECT_NO_THROW({
        ctx.handle_error(Result<void>::error(ResultCode::MpiError, "unit test error", "mock_op"));
    });

    const auto * last = ctx.error_stack().last_error();
    ASSERT_NE(last, nullptr);
    EXPECT_EQ(last->code, ResultCode::MpiError);
    EXPECT_EQ(last->message, std::string("unit test error"));
    EXPECT_EQ(last->operation, std::string("mock_op"));

    Context::destroy_for_comm(MPI_COMM_WORLD);
}

// handle_error：在 ThrowException 下抛出异常，同时写入错误栈
TEST(ResultTest, HandleErrorThrowException)
{
    Environment env;
    env.set_failure_policy(FailurePolicy::LogAndContinue);

    auto & ctx = Context::for_comm(MPI_COMM_WORLD, env);
    ctx.set_failure_policy(FailurePolicy::ThrowException);

    EXPECT_THROW(
        { ctx.handle_error(Result<void>::error(ResultCode::MpiError, "throw path", "mock_op2")); },
        std::runtime_error);

    const auto * last = ctx.error_stack().last_error();
    ASSERT_NE(last, nullptr);
    EXPECT_EQ(last->code, ResultCode::MpiError);
    EXPECT_EQ(last->message, std::string("throw path"));
    EXPECT_EQ(last->operation, std::string("mock_op2"));

    Context::destroy_for_comm(MPI_COMM_WORLD);
}